#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>
using std::endl;
using std::cout;

#define NUM 1024
#define SIZE 32
#define SEP " "

char cmd_line[NUM]; // 保存完整的命令
char* g_argv[SIZE]; // 用来保存打散后的命令行提示符
char g_myval[64]; // 写一个环境变量的buffer, 用来测试

// xshell运行原理, 通过子进程执行命令,让父进程等待 && 解析命令
int main()
{
    //extern char** environ;
    while (1)
    {
        // 1.打印提示符
        cout << "[root@localhost myshell]# ";
        fflush(stdout);
        memset(cmd_line, 0, sizeof cmd_line);
        // 2. 获取用户的键盘输入
        if (fgets(cmd_line, sizeof cmd_line, stdin) == nullptr)
            continue;
        cmd_line[strlen(cmd_line)-1] = '\0';
        cout << cmd_line << endl;
        // 3.命令字符串解析: "ls -a -l" -> "ls", "-a", "-l"
        g_argv[0] = strtok(cmd_line, SEP);
        int index = 1;
        if (strcmp(g_argv[0], "ls") == 0)
            g_argv[index++] = (char*)"--color=auto";
        if (strcmp(g_argv[0], "ll") == 0)
        {
            g_argv[0] = (char*)"ls";
            g_argv[index++] = (char*)"-l";
            g_argv[index++] = (char*)"--color=auto";
        }
        while (g_argv[index++] = strtok(nullptr, SEP)); // 继续解析字符串
        if (strcmp(g_argv[0], "export") == 0 && g_argv[1] != nullptr)
        {
            strcpy(g_myval, g_argv[1]);
            int ret = putenv(g_myval);
            if (ret == 0)
                cout << "export success " << g_argv[1] << endl;
            continue;
        }
        // 4. 内置命令(让父进程自己执行的命令)
        if (strcmp(g_argv[0], "cd") == 0) // 不能让子进程执行,必须是父进程执行
        {
            if (g_argv[1] != nullptr)
            {
                chdir(g_argv[1]); 
                continue;
            }
        }
        // 5. fork()
        pid_t id = fork();
        if (id == 0)
        {
            // 子进程
            cout << "下面功能子进程完成" << endl;
            cout << "child MYVAL: " << getenv("MYVAL") << endl;
            cout << "child PATH: " << getenv("PATH") << endl;

            execvp(g_argv[0], g_argv);
            exit(1);
        }
        // 父进程
        int status = 0;
        pid_t ret = waitpid(-1, &status, 0);
        if (ret > 0)
            cout << "exit code: " << WEXITSTATUS(status) << endl;
    }
    return 0;
}